home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / bipl.zip / PROGS.ZIP / NIM.ICN < prev    next >
Text File  |  1992-09-28  |  9KB  |  311 lines

  1. ############################################################################
  2. #
  3. #    File:     nim.icn
  4. #
  5. #    Subject:  Program to play the game of nim
  6. #
  7. #    Author:   Jerry Nowlin
  8. #
  9. #    Date:     December 24, 1991
  10. #
  11. ###########################################################################
  12. #
  13. # The game of nim focuses on a pile of 15 sticks.  Each player can
  14. # select 1, 2, or 3 sticks from the sticks remaining in the pile when
  15. # it's their turn.  The player to pick up the last stick(s) wins.  The
  16. # loser of the previous game always gets to go first.
  17. #
  18. # There are two versions of nim in here.  The first (default) version
  19. # uses an algorithm to make its moves.  It will never lose if it gets
  20. # the first turn.  The second version tries to learn from each game.
  21. # You'll have to play a few games before it will get very smart but
  22. # after a while it will also never lose if it gets the first turn.  This
  23. # is assuming of course that you know how to play.  Since the learning
  24. # version learns from the person it plays against, if you're lousy the
  25. # game will be too.
  26. #
  27. # To invoke the learning version just pass any argument to the program.
  28. # If you want to see how the program learns, you can use the string
  29. # "show" as the argument and the program's current game memory will be
  30. # displayed after each game.  If you invoke the game with the string save
  31. # as an argument a file called ".nimdump" will be created in the current
  32. # directory with a dump of the program's game memory when you quit and
  33. # the next time the game is played in learn mode it will initialize its
  34. # game memory from the dump.  You can invoke this program with more than
  35. # one argument so show and save can be used at the same time.
  36. #
  37. ############################################################################
  38.  
  39. global    STICKS,    # the number of stick left
  40.     MINE,    # my trys for a given game
  41.     THEIRS,    # their trys for a given game
  42.     TRIED    # the combined tried table (game memory)
  43.  
  44. procedure main(args)
  45.  
  46.     local    resp,    # player response
  47.         turn,    # who's turn
  48.         fp,    # file pointer
  49.         stick,    # sticks index
  50.         take,    # take index
  51.         seed,    # random number seed
  52.         show    # show the game memory flag
  53.  
  54.     # use the current time to define a random number seed
  55.     &random := integer(&clock[1:3] || &clock[4:6] || &clock[7:0])
  56.  
  57.     # check if we should show the thought process of a learning game
  58.     if !args == "show" then show := "yes"
  59.  
  60.     # define game memory
  61.     TRIED := table()
  62.  
  63.     # if this is a learning game and there's a memory dump read it
  64.     if *args > 0 & fp := open(".nimdump","r") then {
  65.         every stick := 1 to 15 do {
  66.             TRIED[stick] := list(3)
  67.             every take := 1 to 3 do
  68.                 TRIED[stick][take] := (read(fp) | "?")
  69.         }
  70.         close(fp)
  71.     }
  72.  
  73.     # otherwise initialize game memory to unknowns
  74.     else every stick := 1 to 15 do TRIED[stick] := [ "?", "?", "?" ]
  75.  
  76.     # start with their turn
  77.     turn := "theirs"
  78.  
  79.     # print the initial message
  80.     write("\nThis is the game of nim.  You must pick up 1, 2 or 3")
  81.     write("sticks from the pile when it's your turn.  The player")
  82.     write("that picks up the last stick(s) wins.  Good luck.")
  83.  
  84.     # loop
  85.     repeat {
  86.  
  87.         # initialize the per game variables
  88.         STICKS := 15
  89.         THEIRS := table()
  90.         MINE := table()
  91.  
  92.         # display the initial stick pile
  93.         dispile()
  94.  
  95.         # loop while there are sticks left
  96.         while STICKS > 0 do
  97.  
  98.             # take turns
  99.             if turn == "theirs" then
  100.                 turn := theirturn(args)
  101.             else    turn := myturn(args)
  102.  
  103.         # the player who took the last stick(s) wins
  104.         if turn == "theirs" then
  105.             write("\nI won!")
  106.         else    write("\nYou won!")
  107.  
  108.         # if this is a thinking game learn from it
  109.         if *args > 0 then learn(turn,show)
  110.  
  111.         # see if they want to play again
  112.         writes("\nDo you want to play again? ")
  113.         if not any('yY',read()) then quit(args,"\nGoodbye.\n")
  114.     }
  115. end
  116.  
  117. procedure theirturn(args)
  118.  
  119.     local    pick    # the players pick
  120.  
  121.     # find out how many sticks they want
  122.     writes("How many sticks do you want? ")
  123.     pick := read()
  124.  
  125.     # check their response to see if they want to quit
  126.     if any('qQ',pick) then quit(args,"\nYou gave up!\n")
  127.  
  128.     # check to see if their pick is valid
  129.     if not numeric(pick) | pick < 1 | pick > (3 | STICKS) then
  130.         write("\007Invalid Response\007\n") & return "theirs"
  131.  
  132.     # save their pick if this is a thinking game
  133.     if *args > 0 then THEIRS[STICKS] := pick
  134.  
  135.     # take away the sticks
  136.     STICKS -:= pick
  137.  
  138.     # if there are any sticks left display them
  139.     if STICKS > 0 then dispile()
  140.  
  141.     # make it my turn
  142.     return "mine"
  143. end
  144.  
  145. procedure myturn(args)
  146.  
  147.     local    pick    # my pick
  148.  
  149.     # let them know I'm about to pick
  150.     writes("I'll take ")
  151.  
  152.     # make my choice depending on whether or not this is a thinking game
  153.     if *args > 0 then {
  154.  
  155.         # think about it
  156.         pick := thinkpick(STICKS)
  157.  
  158.         # if I can't make up my mind randomly pick one choice
  159.         if type(pick) == "list" then pick := ?pick
  160.  
  161.         MINE[STICKS] := pick
  162.  
  163.     } else    pick := algorpick(STICKS)
  164.  
  165.     # tell them what I decided
  166.     write((1 < pick) || " sticks." | "1 stick.")
  167.  
  168.     # take away the sticks
  169.     STICKS -:= pick
  170.  
  171.     # if there are any sticks left display them
  172.     if STICKS > 0 then dispile()
  173.  
  174.     # make it their turn
  175.     return "theirs"
  176. end
  177.  
  178. procedure dispile()
  179.     write()
  180.     every 1 to STICKS do writes("/ ")
  181.     write("\n")
  182. end
  183.  
  184. # Use an algorithmic method to choose the number of sticks I want.  The
  185. # decision is made by taking the number of sticks that will leave an even
  186. # multiple of 4 in the pile (0 is an even multiple of 4) if possible and if
  187. # not then randomly choose 1, 2 or 3 sticks.
  188.  
  189. procedure algorpick(sticks)
  190.     return (0 ~= (sticks % 4)) | ?3
  191. end
  192.  
  193. # Use a learning method to choose the number of sticks I want.  The
  194. # decision is made by looking at the choices that have been made for this
  195. # number of sticks in the past and the results of the game where it was
  196. # made.  If there is no pick that resulted in a win make a random pick
  197. # from all the unknown picks.  If there are no unknown picks just randomly
  198. # choose 1, 2 or 3 sticks and hope THEY screw up.
  199.  
  200. procedure thinkpick(sticks,recurse)
  201.  
  202.     local    picks,    # unknown picks
  203.         take,    # take index
  204.         check,    # check list
  205.         pick    # my pick
  206.  
  207.     # initialize a list of unknown picks
  208.     picks := []
  209.  
  210.     # check every possible pick
  211.     every take := 1 to 3 do {
  212.  
  213.         # if this pick won take it
  214.         if TRIED[sticks][take] == "won" then return take
  215.  
  216.         # if this pick is unknown save it
  217.         if TRIED[sticks][take] == "?" then put(picks,take)
  218.     }
  219.  
  220.     # if there are no unknown picks and no winning picks anything goes
  221.     if *picks = 0 then picks := [1,2,3]
  222.  
  223.     # be smarter and check to see if there is a clear win for THEM
  224.     # after any of the picks left
  225.     if /recurse then {
  226.         check := []
  227.         every pick := !picks do
  228.             if type(thinkpick(0 < (sticks - pick),1)) == "list" then
  229.                 put(check,pick)
  230.         if *check = 0 then
  231.             picks := [1,2,3]
  232.         else    picks := check
  233.     }
  234.  
  235.     return picks
  236. end
  237.  
  238. # Save the results of each pick in this game in the programs game memory and
  239. # if the command line argument was "show" display the updated game memory.
  240.  
  241. procedure learn(turn,show)
  242.  
  243.     local    them,    # their outcome flag
  244.         me,    # my outcome flag
  245.         stick,    # sticks index
  246.         take    # taken index
  247.  
  248.     # decide on the outcome
  249.     if turn == "theirs" then
  250.         them := "lost" & me := "won"
  251.     else    them := "won" & me := "lost"
  252.  
  253.     # check for all the picks made for this game and save the results
  254.     # in the game memory
  255.     every stick := 1 to 15 do {
  256.         if \MINE[stick] then
  257.             TRIED[stick][MINE[stick]] :=
  258.                 comp(TRIED[stick][MINE[stick]],me)
  259.         if \THEIRS[stick] then
  260.             TRIED[stick][THEIRS[stick]] :=
  261.                 comp(TRIED[stick][THEIRS[stick]],them)
  262.     }
  263.  
  264.     # if the show flag is set print the program's game memory
  265.     if \show then {
  266.         writes("\n               picks\n          ")
  267.         every writes(center(1 to 3,5))
  268.         write("\n         ----------------")
  269.         every stick := 15 to 1 by -1 do {
  270.             if stick = 8 then
  271.                 writes("sticks ",right(stick,2),"|")
  272.             else    writes("       ",right(stick,2),"|")
  273.             every take := 1 to 3 do
  274.                 writes(center(TRIED[stick][take],5))
  275.             write()
  276.         }
  277.     }
  278.  
  279.     return
  280. end
  281.  
  282. # Compare this game's result with what the program remembers.  If the results
  283. # were the same fine.  If the old result was unknown save the new result.  If
  284. # the old result is different from the new result the game can't know for
  285. # sure anymore so go back to unknown.
  286.  
  287. procedure comp(old,new)
  288.  
  289.     return (old == new) | (old == "?" & new) | "?"
  290.  
  291. end
  292.  
  293. procedure quit(args,msg)
  294.  
  295.     local    fp,    # file pointer
  296.         stick,    # sticks index
  297.         take    # take index
  298.  
  299.     write(msg)
  300.  
  301.     if !args == "save" then
  302.         if fp := open(".nimdump","w") then {
  303.             every stick := 1 to 15 do
  304.                 every take := 1 to 3 do
  305.                     write(fp,TRIED[stick][take])
  306.             close(fp)
  307.         }
  308.  
  309.     exit()
  310. end
  311.